/**
 * 
 */
package com.couchcrowler.client.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import com.couchcrowler.client.Couchcrowler;
import com.couchcrowler.client.GreetingService;
import com.couchcrowler.client.GreetingServiceAsync;
import com.couchcrowler.client.event.ErrorEvent;
import com.couchcrowler.client.event.LoadingStatusEvent;
import com.couchcrowler.client.event.ProfileLoadedEvent;
import com.couchcrowler.client.event.ProfileLoadedHandler;
import com.couchcrowler.shared.Profile;
import com.couchcrowler.shared.RetryMap;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * @author vienozin
 *
 */
public class ProfileLoader implements ProfileLoadedHandler {
	
	private int maxProfilesToLoad = 20;
	
	private int maxRetries = 20;
	
	private int retryTimeout = 3000;
		
	private RetryMap<String> retries = new RetryMap<String>(maxRetries);
	
	private final GreetingServiceAsync service = GWT.create(GreetingService.class);
	
	private List<String> idsToLoad = new ArrayList<String>();
	
	public ProfileLoader() {
		Couchcrowler.MANAGER.addHandler(ProfileLoadedHandler.type, this);
	}
	
	public void loadProfile(String id) {
		loadProfiles(Arrays.asList(new String[]{id}));
	}
	
	public synchronized void loadProfiles(Collection<String> ids) {
		numberOfLoadingProfiles += ids.size();
		idsToLoad.addAll(ids);
		
		notifyStatus();
		loadProfiles();
	}
	
	private int numberOfLoadingProfiles = 0;
	
	private synchronized void notifyStatus() {
		Couchcrowler.MANAGER.fireEvent(new LoadingStatusEvent(0, numberOfLoadingProfiles, ProfileLoader.class));
	}

	private synchronized void loadProfiles() {
		final List<String> ids = new ArrayList<String>(maxProfilesToLoad);
		while (!idsToLoad.isEmpty() && ids.size() < maxProfilesToLoad) {
			ids.add(idsToLoad.remove(0));
		}
		
		if (ids.isEmpty()) {
			return;
		}
		
		service.getProfiles(ids, new AsyncCallback<List<Profile>>() {
			
			@Override
			public void onSuccess(List<Profile> profiles) {
				for (Profile profile : profiles) {
					if (profile != null) {
						if (!profile.isPrivateProfile()) {
							Couchcrowler.MANAGER.fireEvent(new ProfileLoadedEvent(profile));
						}
						
						ids.remove(profile.getId());
					}
				}
				
				handleMissingIds(ids);
			}
			
			private void handleMissingIds(final Collection<String> ids) {
				if (!ids.isEmpty()) {
					new Timer() {
						
						@Override
						public void run() {
							Collection<String> toRetry = retries.retry(ids);
							
							if (ids.size() != toRetry.size()) {
								numberOfLoadingProfiles -= (ids.size() - toRetry.size());
								notifyStatus();
							}
							
							loadProfiles(toRetry);
						}
					}.schedule(retryTimeout);
				}
			}
			
			@Override
			public void onFailure(Throwable caught) {				
				Couchcrowler.MANAGER.fireEvent(new ErrorEvent(caught));
				
				handleMissingIds(ids);
			}
		});	
		
		loadProfiles();
	}

	@Override
	public void loaded(Profile profile) {
		numberOfLoadingProfiles--;
		notifyStatus();
	}
}
